<template>
    <div class="editor-wrapper">
        <div class="title">{{ props.title }}: {{ file_name }}</div>
        <!-- <codemirror v-model="code" placeholder="Code gose here..." :style="{ height: '390px' }" :tabSize="4" -->
        <div class="editor-main" ref="editorRef">
            <!-- <codemirror v-model="code" placeholder="Code gose here..." :tabSize="4" :extensions="extensions" /> -->
        </div>
        <div class="run">
            <input v-model="compile_params" v-if="show_compile_input" type="text" placeholder="编译参数">
            <input v-model="exec_params" type="text" placeholder="运行参数">
            <button @click="handleRunClick">{{ is_running ? '运行中...' : '运行' }}</button>
        </div>
        <div class="output">
            <p v-for="line in output" :class="output_error ? 'error' : ''">{{ line }}</p>
        </div>
    </div>
</template>


<script lang="ts" setup name="Editor">
import { onMounted, ref, type Ref } from 'vue';
import { EditorState } from "@codemirror/state";
import { EditorView, basicSetup } from "codemirror";
import { cpp } from "@codemirror/lang-cpp";
import { rust } from "@codemirror/lang-rust";
import { dracula } from "@uiw/codemirror-theme-dracula";
import EventBus from '@/event_bus';
import { v4 as uuidv4 } from 'uuid';

import {
    TRANSCODE_CLICKED,
    TRANSCODE_DONE,
    RUST_CODE_UPDATE,
    EVALUATION_CLICKED,
    AUTO_TRANSCODE_DONE,
    CPP_EXEC_DONE,
    RUST_EXEC_DONE,
    CPP_CODE_UPDATE,
    RUST_EXEC_ERROR,
    UPDATE_SERVER_TOOL
} from '@/event_bus/events';
import { transcode, run, transcode_stream, auto_transcode } from '@/api';
import type { RunRequest, TranscodeRequest } from '@/types';
import C_SAMPLE from '@/assets/c_sample';

interface EditerProps {
    title: string,
    lang: 'cpp' | 'rust',
}

const props = withDefaults(defineProps<EditerProps>(), {
    title: 'Editor',
});

const output = ref<Array<string>>([]);
const output_error = ref(false);

const is_running = ref(false);

const compile_params = ref('');
const exec_params = ref('');
const file_name = ref('');
// 保存每次对话的ID, 直到CPP代码更新
// 每次CPP代码更新都会生成一个新的ID
const conversation_id = ref('');

let show_compile_input: Ref<boolean>;
// let extensions: any = [];

const editorRef = ref();
const editorView = ref();

function clearOutput() {
    output.value = [];
    output_error.value = false;
}

onMounted(() => {
    conversation_id.value = uuidv4();
    let extensions: any[] = [];
    let default_code = '// Here shows rust code generated from C!';

    if (props.lang === 'cpp') {
        extensions = [basicSetup, cpp(), dracula];
        default_code = C_SAMPLE[0].code || '';
        file_name.value = C_SAMPLE[0].name;
    } else if (props.lang === 'rust') {
        extensions = [basicSetup, rust(), dracula];
    }

    const startState = EditorState.create({
        doc: default_code,
        extensions,
    });
    if (editorRef.value) {
        editorView.value = new EditorView({
            state: startState,
            parent: editorRef.value,
        });
    }

    if (props.lang === 'cpp') {
        show_compile_input = ref(true);
        const rust_exec_error = ref('');

        EventBus.on(CPP_CODE_UPDATE, (handle: any) => {
            // 清空编译/运行参数
            compile_params.value = handle.compile_params || '';
            exec_params.value = handle.exec_params || '';
            clearOutput();

            conversation_id.value = uuidv4();
            editorView.value.dispatch({
                changes: {from: 0, to: editorView.value.state.doc.length, insert: handle.code}
            })
            file_name.value = handle.name;
        })
    
    
        EventBus.on(RUST_EXEC_ERROR, (content: any) => {
            // console.log('接收到Rust代码执行出错');
            // console.log(content);
            rust_exec_error.value = content;
        })
        /*
            {
                tool: '',
                prompt: ''
                auto_repeat: 1
            }
        */
        EventBus.on(TRANSCODE_CLICKED, async (config: any) => {
            // const prompt = `${config.prompt || '给出的代码错误，根据以下错误信息修改后重新输出'}: \n${output.value.slice(1).join('\n')}`;
    
            let prompt = config.prompt;
    
            // console.log('rust_exec_error.value: ', rust_exec_error.value);
    
            // 代码执行有错误，需要把错误信息作为prompt的一部分
            if (rust_exec_error.value) {
                prompt = `${prompt}，以下是错误信息：\n${rust_exec_error.value}`;
                rust_exec_error.value = '';
            }
    
            // clearOutput();
    
            // console.log(`With full prompt: ${prompt}`);
    
            const data: TranscodeRequest = {
                prompt,
                tool: config.tool,
                compile_params: compile_params.value,
                exec_params: exec_params.value,
                auto_repeat: config.auto_repeat || 1,
                code: editorView.value.state.doc.toString(),
                id: conversation_id.value,
            }

            console.log('transcode request data:', data);

            // 以下代码使用transcode api ============================
            // if (config.tool === 'c2rust') {
            if (['c2rust', 'chatgpt4', 'auto'].includes(config.tool)) {
                try {
                    let response = null;

                    if (config.tool === 'auto') {
                        response = await auto_transcode(data);
                    } else {
                        response = await transcode(data);
                    }

                    // const response = await transcode(data);
        
                    console.log('transcode response data:', response);
                    if (response.status == 0) {
                        output_error.value = false;
                        EventBus.emit(RUST_CODE_UPDATE, response.content);
                        EventBus.emit(UPDATE_SERVER_TOOL, response.tool);
                    } else {
                        console.error(response.error);
                        output_error.value = true;
                        output.value = (response.error?.toString() || '').split('\n');
                    }
                } catch(error: any) {
                    console.error(error.message);
                    output_error.value = true;
                    output.value = (error.message || '').split('\n');
                }
        
                EventBus.emit(TRANSCODE_DONE, null);
                if (config.is_auto) {
                    EventBus.emit(AUTO_TRANSCODE_DONE, null);
                }
                return;
            }

            // 以上代码使用transcode api ============================

    
            // 流式接收数据
            let content = '';
            let full_content = '';
            let started = false;
            let finished = false;
            transcode_stream(
                data,
                (response) => {
                    // EventBus.emit(RUST_CODE_UPDATE, response.content);
                    full_content += response.content;
                    // console.log('transcode stream response data:', response);
                    if (!finished) {
                        content += response.content;
                    }
                    // 只在编辑器中显示代码
                    if (content.indexOf('```rust') >= 0) {
                        // console.log('开始截取代码');
                        
                        content = content.split('```rust')[1];
                        started = true;
                    }

                    // EventBus.emit(RUST_CODE_UPDATE, response.content);
                    if (started && !finished) {
                        if (content.indexOf('```') >= 0) {
                            // console.log('停止截取代码');
                            content = content.split('```')[0].trim();
                            finished = true;
                        }
                        EventBus.emit(RUST_CODE_UPDATE, content);
                    }
                },
                () => {
                    console.log('转译完成！');
                    console.log(full_content);
                    // EventBus.emit(RUST_CODE_UPDATE, full_content);

                    // ------------------------------------
                    // 从markdown中提取rust代码
                    const reg = /```rust\n([\s\S]*?)```/g;
                    const codes = [];
                    let result: any = '';
                    while (result = reg.exec(full_content)) {                        
                        codes.push(result[1]);
                    }
                    EventBus.emit(RUST_CODE_UPDATE, codes[0].trim());
                    // ------------------------------------

                    EventBus.emit(TRANSCODE_DONE, null);

                    if (config.is_auto) {
                        EventBus.emit(AUTO_TRANSCODE_DONE, null);
                    }
                }
            )
        })
    } else if (props.lang === 'rust') {
        show_compile_input = ref(false);

        EventBus.on(RUST_CODE_UPDATE, (rust_code: any) => {
            editorView.value.dispatch({
                // changes: {from: editorView.value.state.doc.length, insert: rust_code},
                changes: {from: 0, to: editorView.value.state.doc.length, insert: rust_code},
            })
        })
    
        // 点击转译按钮的时候, 将错误信息传递给C编辑器，清空output和rust代码
        EventBus.on(TRANSCODE_CLICKED, async (_tool: any) => {
            clearOutput();
            editorView.value.dispatch({
                changes: {from: 0, to: editorView.value.state.doc.length, insert: ''}
            })
        })
    
        // C文件切换的时候, 清空rust代码和output
        EventBus.on(CPP_CODE_UPDATE, (handle: any) => {
            // 清除错误信息
            EventBus.emit(RUST_EXEC_ERROR, '');
            // 清空rust代码和output
            editorView.value.dispatch({
                changes: {from: 0, to: editorView.value.state.doc.length, insert: default_code}
            })
            clearOutput();
            // 清空编译/运行参数
            compile_params.value = handle.compile_params || '';
            exec_params.value = handle.exec_params || '';
        })
    }
})

async function handleRunClick() {
    if (is_running.value) return;
    is_running.value = true;    
    await handleRun();
    is_running.value = false;
}

async function handleRun() {
    // if (is_running.value) return;
    // is_running.value = true;
    clearOutput();
    if (props.lang === 'cpp') {
        console.log('Run cpp clicked.');
        console.log('With compile params: ' + compile_params.value);
        console.log('With exec params: ' + exec_params.value);
        output.value = [
            'Compile with command: g++ ./source.cpp -o target ' + compile_params.value,
            'Exec with command: ./target ' + exec_params.value,
        ];
    } else if (props.lang === 'rust') {
        console.log('Run rust clicked.');
        console.log('With exec params: ' + exec_params.value);
        output.value = [
            'Exec with command: cargo run -q -- ' + exec_params.value,
        ];
    }

    const data: RunRequest = {
        language: props.lang,
        code: editorView.value.state.doc.toString(),
        compile_params: compile_params.value,
        exec_params: exec_params.value
    }

    console.log('exec request data:', data);

    // 清空编译/运行参数
    // compile_params.value = '';
    // exec_params.value = '';

    // 记录执行结果并最终返回，用于在测评中进行对比
    let exec_output = '';
    try {
        const response = await run(data);

        console.log('exec response data:', response);
        if (response.status == 0) {
            output_error.value = false;
        } else {
            output_error.value = true;
        }
        exec_output = response.output;
        output.value = output.value.concat((response.output || '').split('\n'));
    } catch (error: any) {
        console.error(error.message);
        output_error.value = true;
        exec_output = (error.message || '');
        output.value = output.value.concat((error.message || '').split('\n'));
    }

    // 将错误信息发送到c编辑器，方便c在转译时带上错误信息
    if (props.lang === 'rust') {
        if (output_error.value) {
            EventBus.emit(RUST_EXEC_ERROR, exec_output);
        } else {
            // 清除错误信息
            EventBus.emit(RUST_EXEC_ERROR, '');
        }
    }

    // is_running.value = false;
    return exec_output;
}


/*
{
    auto_repeat: 1
    repeat_remain: 1,
    server_tool: 
    tool: '',
    prompt: ''
}
*/

// 自动转译，运行，评估，多轮询问

if (props.lang === 'cpp') {
    EventBus.on(EVALUATION_CLICKED, async function(config: any) {
        console.warn('开始自动测评......');
        console.warn('开始转译......');
        EventBus.emit(TRANSCODE_CLICKED, {
            is_auto: true,
            prompt: '',
            ...config
        });
        console.log(config);

        if (config.auto_repeat === config.repeat_remain) {
            console.warn('cpp开始执行......');
            const exec_output = await handleRun();
            console.warn('cpp执行完成......');
    
            EventBus.emit(CPP_EXEC_DONE, {
                exec_output,
                exec_code: editorView.value.state.doc.toString(),
            });
        }
    })
} else if (props.lang === 'rust') {
    EventBus.on(AUTO_TRANSCODE_DONE, async () => {
        console.warn('转译完成......');
        console.warn('rust开始执行......');

        const exec_output = await handleRun();
        console.warn('rust执行完成......');

        EventBus.emit(RUST_EXEC_DONE, {
            exec_output,
            exec_code: editorView.value.state.doc.toString(),
        });
    })
}


</script>

<style scoped>
.editor-wrapper {
    /* height: 550px; */
    height: 100%;
    width: 50%;
    background-color: rgba(42, 42, 42, 1);
    /* border: 2px solid #ccc; */
    /* border-radius: 8px 8px 0 0; */
    /* box-shadow: 0px 0px 10px #bbb; */
    /* height: calc(100vh - 50px); */
}

.editor-wrapper {
    border-left: 1px solid rgb(189, 147, 249);
}

.editor-main {
    height: calc(100% - 234px);
    min-height: 278px;
}

.title {
    font-size: 18px;
    padding: 0.2em;
    background-color: #191a21;
    color: rgba(255, 255, 255, .5);
    text-indent: 1em;
    user-select: none;
}

.output {
    width: 100%;
    height: 160px;
    padding: 10px;
    overflow-y: scroll;
    border-top: 1px solid rgb(189, 147, 249);
    background-color: rgb(40, 42, 54);
    color: rgba(255, 255, 255, .5);
    font-family: 'Consolas';
}

.output::-webkit-scrollbar {
    width: 8px;
}

.output::-webkit-scrollbar-thumb {
    background: rgb(55, 57, 64);
    border-radius: 4px;
}

.output p.error {
    color: rgb(255, 85, 85)
}

.run {
    border-top: 1px solid rgb(189, 147, 249);
    width: 100%;
    display: flex;
    flex-direction: row;
}

.run button {
    border: 0;
    background-color: rgb(62, 64, 74);
    padding: 8px 12px;
    box-shadow: 0 0 6px rgb(62, 64, 74);
    color: rgba(255, 255, 255, .5);
    cursor: pointer;
    font-size: 16px;
    width: 16%;
    flex-grow: 0;
}

.run button:active {
    background-color: rgb(33, 34, 44);
}

.run input {
    height: 36px;
    outline: none;
    border: none;
    font-size: 16px;
    padding-left: .5em;
    background-color: rgb(40, 42, 54);
    color: rgba(255, 255, 255, .5);
    flex-grow: 1;
}

.run input:first-child {
    border-right: 1px solid rgb(189, 147, 249);
}

.run input:active {
    outline: none;
}
</style>